﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

#pragma once

// qt headers
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QJsonArray>
#include <QJsonParseError>

// custom headers
#include "GcsService.h"
#include "IResponseHandler.h"
#include "IGcsLogger.h"


namespace gmepgcs
{
    template<typename TResult>
    class ResponseHandlerBase : public IResponseHandler<TResult>
    {
    public:
        explicit ResponseHandlerBase();
        virtual ~ResponseHandlerBase();

    public:
        virtual TResult Handle(QNetworkReply *pReply, std::wstring & errorMessage) override;

    protected:
        virtual void HandleData(const QJsonValue &data) = 0;

    private:
        void PrivateHandle(QNetworkReply * pReply);
        void HandleBody(const QJsonObject &body);

    protected:
        TResult m_result;
        std::wstring m_errorMessage;
    };


    template<typename TResult>
    ResponseHandlerBase<TResult>::ResponseHandlerBase() :
        m_result(),
        m_errorMessage()
    {
    }

    template<typename TResult>
    ResponseHandlerBase<TResult>::~ResponseHandlerBase()
    {
    }

    template<typename TResult>
    TResult ResponseHandlerBase<TResult>::Handle(QNetworkReply * pReply, std::wstring & errorMessage)
    {
        m_result = TResult();
        m_errorMessage = std::wstring();

        PrivateHandle(pReply);

        errorMessage = m_errorMessage;
        return m_result;
    }
    
    template<typename TResult>
    void ResponseHandlerBase<TResult>::PrivateHandle(QNetworkReply * pReply)
    {
        if (pReply == nullptr)
        {
            m_errorMessage = L"Reply is null!";
            return;
        }
        
        if (pReply->error() != QNetworkReply::NoError)
        {
            int32_t statusCode = pReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
            auto errorStr = pReply->errorString();
            QString qErrorMessage = QString("request error: http code - %1, message - %2").arg(statusCode).arg(errorStr);
            IGcsLogger::Get()->Error({ "fail to get response from " + pReply->url().toString() + "\r\n",  qErrorMessage });

            m_errorMessage = qErrorMessage.toStdWString();
            return;
        }

        QByteArray jsonResponseBodyBytes = pReply->readAll();
        IGcsLogger::Get()->Info({
            "succeed to get response from " + pReply->url().toString() + "\r\n",
            "response body:" + QString::fromUtf8(jsonResponseBodyBytes) 
        });

        QJsonParseError parseError;
        QJsonDocument jsonResponseBodyDoc = QJsonDocument::fromJson(jsonResponseBodyBytes, &parseError);
        if (parseError.error != QJsonParseError::ParseError::NoError)
        {
            QString qErrorMessage = QString("fail to parse response body:error type - %1").arg((int32_t)parseError.error);
            IGcsLogger::Get()->Error(qErrorMessage);

            m_errorMessage = qErrorMessage.toStdWString();
            return;
        }

        QJsonObject jsonResponseBody = jsonResponseBodyDoc.object();
        HandleBody(jsonResponseBody);
    }

    template<typename TResult>
    inline void ResponseHandlerBase<TResult>::HandleBody(const QJsonObject &body)
    {
        QString code = body["code"].toString();
        QString message = body["message"].toString();
        if (code.trimmed().toLower() != "success")
        {
            QString qErrorMessage = QString("internal request error: custom code - %1, message - %2").arg(code).arg(message);
            IGcsLogger::Get()->Error(qErrorMessage);

            m_errorMessage = qErrorMessage.toStdWString();
            return;
        }

        QJsonValue data = body["data"];
        HandleData(data);
    }

}